/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


// s_occlusion.cpp -- sound portal occlusion


#include "s_local.h"


#define PORTAL_DISTANCE			200.0f

typedef struct {
	bool		visited[BSP_MAX_AREAS];

	float		distance;
} portalFlow_t;


/*
 ==================
 S_PortalFlow
 ==================
*/
static void S_PortalFlow (int area, float distance, portalFlow_t *portalFlow){

	areaPortal_t	areaPortal;
	int				nextArea;
	int				i;

	if (portalFlow->distance <= distance)
		return;		// Already found a shorter route

	// If we found the listener area, store the distance
	if (area == snd.listener.area){
		portalFlow->distance = distance;
		return;
	}

	// Check all the portals in this area
	for (i = 0; i < CM_NumPortalsInArea(area); i++){
		CM_GetPortalFromArea(area, i, &areaPortal);

		// Determine which area this portal leads to
		if (areaPortal.areas[0] == area)
			nextArea = areaPortal.areas[1];
		else if (areaPortal.areas[1] == area)
			nextArea = areaPortal.areas[0];
		else
			continue;

		if (portalFlow->visited[nextArea])
			continue;		// Already visited this area
		portalFlow->visited[nextArea] = true;

		snd.pc.portals++;

		// Recurse down the next area
		if (areaPortal.open)
			S_PortalFlow(nextArea, distance, portalFlow);
		else
			S_PortalFlow(nextArea, distance + PORTAL_DISTANCE, portalFlow);

		portalFlow->visited[nextArea] = false;
	}
}

/*
 ==================
 S_SoundIsOccluded
 ==================
*/
bool S_SoundIsOccluded (int area, float distance, float maxDistance, float *occludedDistance){

	portalFlow_t	portalFlow;

	// If outside the map, occlude the sound
	if (snd.listener.area == -1 || area == -1){
		*occludedDistance = maxDistance;
		return true;
	}

	// If in the listener area, don't occlude it
	if (snd.listener.area == area)
		return false;

	// If the areas are connected, don't occlude it
	if (CM_AreasAreConnected(snd.listener.area, area))
		return false;

	// Flow through the portals
	memset(portalFlow.visited, 0, sizeof(portalFlow.visited));

	portalFlow.visited[area] = true;
	portalFlow.distance = maxDistance;

	S_PortalFlow(area, distance, &portalFlow);

	// Is it occluded?
	if (portalFlow.distance <= distance)
		return false;

	*occludedDistance = Min(portalFlow.distance, maxDistance);

	return true;
}
